package com.xmutca.core.websocket.handler;

import com.xmutca.core.cache.CacheObject;
import com.xmutca.core.cache.J2Cache;
import com.xmutca.core.websocket.ChannelFactory;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

@ChannelHandler.Sharable
public class FullHttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

	private final static String URL = "/websocket/";
	private final static String CACHE_KEY = "auth-ticket:%s";

	private final static Logger logger = LoggerFactory.getLogger(FullHttpRequestHandler.class);

	private ChannelFactory channelFactory;

	private WebSocketEventHandler webSocketEventHandler;

	private TextWebSocketFrameHandler textWebSocketFrameHandler;

	@Override
	protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
		//失败回执，1.情况有解码失败 2.协议不是websocket，3.请求的url是伪造的
		if (!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade"))) || req.uri().indexOf(URL) == -1) {
			sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
			return;
		}

		//获取用户信息ticket
		String ticket = req.uri().substring(URL.length());

		//用户的ticket为空,说明是恶意请求
		if (!StringUtils.hasText(ticket)) {
			sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
			return;
		}

		CacheObject memberIdCache = J2Cache.getInstance().get(String.format(CACHE_KEY, ticket));
		//用户的memberIdCache为空,说明是恶意请求,或者该ticket已被使用
		if (null == memberIdCache.getValue()) {
			sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
			return;
		}

		//删除缓存中的用户信息
		J2Cache.getInstance().evict(ticket);

		//返还握手状态
		WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null, false);
		WebSocketServerHandshaker handShaker = wsFactory.newHandshaker(req);
		channelFactory.setHandShaker(handShaker);
		if (handShaker == null) {
			WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
		} else {
			channelFactory.openHandshake(ctx, req);
		}

		//保存session信息
		channelFactory.putSession((String) memberIdCache.getValue(), ctx.channel());

		if (webSocketEventHandler == null) {
			webSocketEventHandler = new WebSocketEventHandler();
		}

		//处理打开事件
		webSocketEventHandler.open(textWebSocketFrameHandler, (String) memberIdCache.getValue());
		logger.debug("用户：{}, IP:{}, 进入webSocket服务器", memberIdCache.getValue(), ctx.channel().localAddress());
	}

	private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
		// 返回应答给客户端
		if (res.status().code() != 200) {
			ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
			res.content().writeBytes(buf);
			buf.release();
		}

		// 如果是非Keep-Alive，关闭连接
		ChannelFuture f = ctx.channel().writeAndFlush(res);
		if (res.status().code() != 200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}
	}

	// 获取完整的请求路径
	private static String getWebSocketLocation(HttpRequest req) {
		Character host = req.headers().getChar("Host");
		String uri = req.uri();
		return String.format("ws://%s/%s", host, uri);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		logger.debug("用户IP：{} -> 注册WebSocket服务器过程中发生异常", ctx.channel().localAddress());

		ctx.close();
	}

	public void setChannelFactory(ChannelFactory channelFactory) {
		this.channelFactory = channelFactory;
	}

	public void setWebSocketEventHandler(WebSocketEventHandler webSocketEventHandler) {
		this.webSocketEventHandler = webSocketEventHandler;
	}

	public void setTextWebSocketFrameHandler(TextWebSocketFrameHandler textWebSocketFrameHandler) {
		this.textWebSocketFrameHandler = textWebSocketFrameHandler;
	}
}